﻿/*****************************************************************************************************************************************************
 *   
 *   说明：
 *   
 *   完整报文的格式如下所示：
 *    ___________________________________________________________________
 *   |    SOF       DEST         LEN       TYPE    MACPayload     FCS   |
 *   | (1 byte)   (2 bytes)    (1 byte)  (1 byte)  (LEN byte)  (1 byte) |
 *    -------------------------------------------------------------------
 *   ParkingOriginalPacket类:  对完整报文的解析，完成FCS校验，完成DATA数据的提取，提取出的DATA继续进行解析，然后通过Event机制交给相应的事件处理函数处理
 *                             完整报文的解析原理是不停的搜索SOF头。
 *                             用户在应用程序中需要如下定义自己的报文接收处理函数，系统能自动调用处理，如下所示:
 *                             ParkingOriginalPacket.EvProcessReceivedPacket += sp_EvProcessReceivedPacket; // sp_EvProcessReceivedPacket就是用户自己定义的事件处理函数
 *   时间：2016/4/5
 * 
 *   作者：zhaokang
 * 
 *****************************************************************************************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LRSystem;
using System.Threading;
using System.IO.Ports;
using System.Net.Sockets;

namespace LRSystem
{

    public static class LoWOTOriginalPacket
    {
        static AutoResetEvent autoEvent = new AutoResetEvent(false); // 控制线程用的
        static Queue<baseReceivedPacket> pkqueue;                    // 收到的报文队列

        // 数据包接收Event函数，由用户自己定义
        // response of cmd.
        public delegate void ProcessPacketDelegate(baseReceivedPacket pk);
        public static event ProcessPacketDelegate EvProcessReceivedPacket;

        // Static 构造函数
        static LoWOTOriginalPacket()
        {
            pkqueue = new Queue<baseReceivedPacket>(); // 创建报文队列
            Thread pkproc = new Thread(PacketThread);  // 报文处理线程
            pkproc.IsBackground = true;
            pkproc.Start();
        }

        private static void PacketThread()
        {
            baseReceivedPacket pk;
            while (true)
            {
                autoEvent.WaitOne();              // 阻止线程.可降低CPU占有率，提高性能
                while (pkqueue.Count > 0)
                {
                    pk = pkqueue.Dequeue();

                    NotifyEventReceivedPacket(pk);

                }

            }
        }

        // Notify function.
        private static void NotifyEventReceivedPacket(baseReceivedPacket pk)    // 产生数据到达事件
        {
            if (EvProcessReceivedPacket != null)
                EvProcessReceivedPacket(pk);                         // 调用事件处理
        }

        //public static byte[] BuildOriginalPacket(byte[] dataBuffer, ushort cmd) // 对DATA应用数据进行头、尾封装
        //{
        //    byte fcs = 0;
        //    byte[] buffer = new byte[dataBuffer.Length + (int)PacketFieldLen.FIXLEN];  // 需要将SOF,LEN,CMD,FCS加进去
        //    for (int i = 0; i < dataBuffer.Length; i++)
        //        buffer[(int)PacketFieldPos.DATA0 + i] = dataBuffer[i];
        //    // 封装头
        //    buffer[(int)PacketFieldPos.SOF] = (byte)PacketField.SOF;
        //    buffer[(int)PacketFieldPos.LEN] = (byte)dataBuffer.Length;
        //    buffer[(int)PacketFieldPos.CMD0] = (byte)cmd;
        //    buffer[(int)PacketFieldPos.CMD1] = (byte)(cmd>>8);
            
        //    // 封装尾
        //    for (int i = (int)PacketFieldPos.LEN; i < dataBuffer.Length + (int)PacketFieldPos.DATA0; i++)
        //        fcs ^= buffer[i];
        //    buffer[(int)PacketFieldPos.FCS + dataBuffer.Length] = fcs;
        //    return buffer;
        //}
        
        public static void PacketParse(object sender, List<byte> rxPacket)
        {
            ushort dest, dest0, dest1;
            byte len, type;
            byte fcs;
            bool res;

            while (rxPacket.Count > 0)
            {
                int headIndex = rxPacket.IndexOf((int)PacketField.SOF);                                           // 找到第一个SOF，返回所在的位置

                if (headIndex == -1)
                {
                    rxPacket.Clear();                                                                             // 如果没有找到一个SOF,则清空数据
                }

                if (headIndex > 0)                                                                                // 如果SOF之前还有数据，也就如果数据不是从SOF开始的
                {
                    rxPacket.RemoveRange(0, headIndex);                                                           // 移除SOF之前的数据
                }

                if (rxPacket.Count >= (int)PacketFieldLen.FIXLEN)
                {
                    fcs = rxPacket[(int)PacketFieldPos.DEST0];
                    len = rxPacket[(int)PacketFieldPos.LEN];                                                 // 初始化fcs和len,len为DATA字段长度,fcs从LEN字段开始计算(不包括FCS)
                    dest0 = (ushort)rxPacket[(int)PacketFieldPos.DEST0];
                    dest1 = (ushort)rxPacket[(int)PacketFieldPos.DEST1];
                    dest = (ushort)(dest0 + (dest1 << 8));                                                   // 获取CMD
                    type = rxPacket[(int)PacketFieldPos.TYPE];
                    if (rxPacket.Count >= len + (int)PacketFieldLen.FIXLEN)                                  // 数据帧长度足够长
                    {
                        for (int i = 2; i < len + (int)PacketFieldLen.FIXLEN; i++)                           // 计算FCS,从DEST0字段开始到MACPayload的最后一个字节
                        {
                            fcs ^= rxPacket[i];
                        }
                        if (fcs == 0)                                                                             // 判断FCS是否正确
                        {
                            baseReceivedPacket rsp = (baseReceivedPacket)PacketClassDictionary.CreateInstanse(type); // 根据类型创建报文实例
                            if (rsp != null)
                            {
                                // rsp.rxtime = DateTime.Now; 可以不需要赋值，因为默认构造函数是当前时刻;
                                rsp.sender = sender;
                                if (sender is TcpClient)
                                    rsp.sourcename = ((TcpClient)sender).Client.RemoteEndPoint.ToString();
                                if (sender is SerialPort)
                                    rsp.sourcename = ((SerialPort)sender).PortName;
                                res = rsp.Parse(rxPacket, (int)PacketFieldPos.MACPAYLOAD);                 // 此处首先对帧进行初步解析，进一步形成报文实例
                                if (res)
                                {
                                    pkqueue.Enqueue(rsp);
                                    autoEvent.Set();
                                }
                                rxPacket.RemoveRange(0, len + (int)PacketFieldLen.FIXLEN);           // 清空完整的一帧，进入新一轮循环

                            }
                            else
                            {
                                rxPacket.RemoveRange(0, 1);
                            }
                        }
                        else
                        {
                            rxPacket.RemoveRange(0, 1);                                                           // 如果FCS不正确，则清空SOF头,进入新一轮循环
                        }
                    }
                    else
                    {
                        break;                                                                                    // 数据帧不够长则直接退出,不对rxPack清空处理,不进入下一轮循环,等待新数据上来补充完整,
                    }
                }
                else
                {
                    break;                                                                                        // 数据帧太短,不对rxPack清空处理,不进入下一轮循环,等待新数据上来补充完整,
                }
            }//while 
        }
    }
}
